package com.numberone.iotplatform.service.impl;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.numberone.iotplatform.dto.*;
import com.numberone.iotplatform.entity.*;
import com.numberone.iotplatform.exception.ResourceNotFoundException;
import com.numberone.iotplatform.repository.DeviceRepository;
import com.numberone.iotplatform.repository.DiscoveredDeviceRepository;
import com.numberone.iotplatform.repository.LocationRepository;
import com.numberone.iotplatform.service.AiBoxApiService;
import com.numberone.iotplatform.service.DeviceService;
import com.numberone.iotplatform.service.LocationService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import javax.persistence.criteria.Predicate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Set;
import java.util.UUID;

/**
 * 设备服务实现类，提供设备管理相关功能。
 * 所有代码已确认兼容 JDK 1.8。
 */
@Service
public class DeviceServiceImpl implements DeviceService {

    @Autowired
    private DeviceRepository deviceRepository;

    @Autowired
    private ObjectMapper objectMapper;

    @Autowired
    private LocationService locationService;

    @Autowired
    private LocationRepository locationRepository;

    @Autowired
    private DiscoveredDeviceRepository discoveredDeviceRepository;

    @Autowired
    private AiBoxApiService aiBoxApiService;

    /**
     * 查询设备列表，支持关键字、状态和位置过滤，分页返回。
     */
    @Override
    @Transactional(readOnly = true)
    public Page<DeviceDTO> findAll(String keyword, String status, Long locationId, Pageable pageable) {
        Specification<Device> spec = (root, query, cb) -> {
            List<Predicate> predicates = new ArrayList<>();
            if (StringUtils.hasText(keyword)) {
                predicates.add(cb.or(
                        cb.like(root.get("name"), "%" + keyword + "%"),
                        cb.like(root.get("deviceUid"), "%" + keyword + "%"),
                        cb.like(root.get("sn"), "%" + keyword + "%")
                ));
            }
            if (StringUtils.hasText(status)) {
                predicates.add(cb.equal(root.get("status"), status));
            }
            if (locationId != null) {
                // TODO: 完善为按位置树的子节点查询
                predicates.add(cb.equal(root.get("location").get("id"), locationId));
            }
            return cb.and(predicates.toArray(new Predicate[0]));
        };

        Page<Device> devicePage = deviceRepository.findAll(spec, pageable);
        return devicePage.map(this::entityToDto);
    }

    /**
     * 将 Device 实体转换为 DeviceDTO（用于列表页）。
     */
    private DeviceDTO entityToDto(Device device) {
        DeviceDTO dto = new DeviceDTO();
        dto.setId(device.getId());
        dto.setDeviceUid(device.getDeviceUid());
        dto.setName(device.getName());
        dto.setModel(device.getModel());
        dto.setIpAddress(device.getIpAddress());
        dto.setPort(device.getPort());
        dto.setStatus(device.getStatus());
        if (device.getLocation() != null) {
            dto.setLocationName(device.getLocation().getName());
        } else {
            dto.setLocationName("-");
        }
        return dto;
    }

    /**
     * 获取设备详细信息。
     */
    @Override
    @Transactional(readOnly = true)
    public DeviceDetailDTO getDeviceDetailById(Long id) {
        Device device = deviceRepository.findById(id)
                .orElseThrow(() -> new ResourceNotFoundException("Device not found with id: " + id));
        return entityToDetailDto(device);
    }

    /**
     * 获取设备的通道列表。
     */
    @Override
    public Set<Channel> findChannelsByDeviceId(Long deviceId) {
        Device device = deviceRepository.findById(deviceId)
                .orElseThrow(() -> new ResourceNotFoundException("Device not found with id: " + deviceId));
        return device.getChannels();
    }

    /**
     * 获取设备的模型列表。
     */
    @Override
    public Set<AiModel> findAiModelsByDeviceId(Long deviceId) {
        Device device = deviceRepository.findById(deviceId)
                .orElseThrow(() -> new ResourceNotFoundException("Device not found with id: " + deviceId));
        return device.getAiModels();
    }

    /**
     * 创建新设备。
     */
    @Override
    @Transactional
    public DeviceDTO createDevice(DeviceFormDTO dto) {
        Device device = new Device();
        formDtoToEntity(dto, device);
        Device savedDevice = deviceRepository.save(device);
        return entityToDto(savedDevice);
    }

    /**
     * 更新现有设备。
     */
    @Override
    @Transactional
    public DeviceDTO updateDevice(Long id, DeviceFormDTO dto) {
        Device device = deviceRepository.findById(id)
                .orElseThrow(() -> new ResourceNotFoundException("Device not found with id: " + id));
        formDtoToEntity(dto, device);
        Device updatedDevice = deviceRepository.save(device);
        return entityToDto(updatedDevice);
    }

    /**
     * 将 Device 实体转换为 DeviceDetailDTO（用于详情页）。
     */
    private DeviceDetailDTO entityToDetailDto(Device device) {
        DeviceDetailDTO dto = new DeviceDetailDTO();
        dto.setId(device.getId());
        dto.setName(device.getName());
        dto.setDeviceUid(device.getDeviceUid());
        dto.setSn(device.getSn());
        dto.setModel(device.getModel());
        dto.setIpAddress(device.getIpAddress());
        dto.setPort(device.getPort());
        dto.setDescription(device.getDescription());
        dto.setMaintainer(device.getMaintainer());
        dto.setInstallDate(device.getInstallDate());
        dto.setProductionDate(device.getProductionDate());
        dto.setCreatedAt(device.getCreatedAt());
        dto.setUpdatedAt(device.getUpdatedAt());

        if (device.getLocation() != null) {
            dto.setLocationName(locationService.buildLocationPath(device.getLocation()));
            dto.setLocation(device.getLocation());
        }

        try {
            if (StringUtils.hasText(device.getPhotos())) {
                dto.setPhotos(objectMapper.readValue(device.getPhotos(), new TypeReference<List<String>>() {}));
            } else {
                dto.setPhotos(Collections.emptyList());
            }
        } catch (JsonProcessingException e) {
            dto.setPhotos(Collections.emptyList());
        }

        // 模拟动态运行状态
        dto.setStatus(device.getStatus());
        dto.setLastOnline(LocalDateTime.now().minusMinutes(5));
        dto.setHardwareVersion("21a_V1.0.5_20211029");
        dto.setSoftwareVersion("V1.0.3_20220607");
        dto.setCpuUsage(Math.round(Math.random() * 1000) / 10.0);
        dto.setMemoryUsage(Math.round(Math.random() * 1000) / 10.0);
        dto.setRuntimeDuration("15天 4小时 32分钟");

        return dto;
    }

    /**
     * 将 DeviceFormDTO 映射到 Device 实体。
     */
    private void formDtoToEntity(DeviceFormDTO dto, Device device) {
        device.setName(dto.getName());
        device.setDeviceUid(dto.getDeviceUid());
        device.setSn(dto.getSn());
        device.setModel(dto.getModel());
        device.setProductionDate(dto.getProductionDate());
        device.setInstallDate(dto.getInstallDate());
        device.setIpAddress(dto.getIpAddress());
        device.setPort(dto.getPort());
        device.setUsername(dto.getUsername());
        if (StringUtils.hasText(dto.getPassword())) {
            device.setPassword(dto.getPassword()); // 注意：实际应加密
        }
        device.setDescription(dto.getDescription());
        device.setMaintainer(dto.getMaintainer());

        if (device.getId() == null) {
            device.setStatus("UNINITIALIZED");
        }

        Location location = locationRepository.findById(dto.getLocationId())
                .orElseThrow(() -> new ResourceNotFoundException("Location not found with id: " + dto.getLocationId()));
        device.setLocation(location);

        try {
            if (dto.getPhotos() != null) {
                device.setPhotos(objectMapper.writeValueAsString(dto.getPhotos()));
            } else {
                device.setPhotos(null);
            }
        } catch (JsonProcessingException e) {
            throw new RuntimeException("Failed to serialize photos to JSON", e);
        }
    }

    /**
     * 批量导入设备。
     */
    @Override
    @Transactional
    public void batchImportDevices(BatchImportRequestDTO request) {
        Location location = locationRepository.findById(request.getLocationId())
                .orElseThrow(() -> new ResourceNotFoundException("Location not found with id: " + request.getLocationId()));

        List<DiscoveredDevice> devicesToImport = discoveredDeviceRepository.findAllById(request.getDiscoveredDeviceIds());

        if (devicesToImport.size() != request.getDiscoveredDeviceIds().size()) {
            throw new ResourceNotFoundException("Some discovered devices were not found.");
        }

        List<Device> newDevices = new ArrayList<>();
        for (DiscoveredDevice discovered : devicesToImport) {
            if ("IMPORTED".equals(discovered.getStatus())) {
                continue;
            }

            Device newDevice = new Device();
            newDevice.setName("新设备-" + discovered.getIpAddress());
            newDevice.setIpAddress(discovered.getIpAddress());
            newDevice.setPort(discovered.getPort());
            newDevice.setModel(discovered.getModel());
            newDevice.setLocation(location);
            newDevice.setMaintainer(request.getMaintainer());
            newDevice.setStatus("UNINITIALIZED");
            newDevice.setDeviceUid("UID-" + UUID.randomUUID().toString());
            newDevice.setSn("SN-" + UUID.randomUUID().toString());

            newDevices.add(newDevice);
            discovered.setStatus("IMPORTED");
        }

        deviceRepository.saveAll(newDevices);
        discoveredDeviceRepository.saveAll(devicesToImport);
    }

    /**
     * 重启设备。
     */
    @Override
    public void rebootDeviceById(Long id) {
        Device device = deviceRepository.findById(id)
                .orElseThrow(() -> new ResourceNotFoundException("Device not found with id: " + id));

        boolean success = aiBoxApiService.rebootDevice(device.getIpAddress(), device.getPort());

        if (!success) {
            throw new RuntimeException("Failed to send reboot command to device: " + device.getName());
        }
    }

    /**
     * 删除设备。
     */
    @Override
    @Transactional
    public void deleteDeviceById(Long id) {
        if (!deviceRepository.existsById(id)) {
            throw new ResourceNotFoundException("Device not found with id: " + id);
        }

        deviceRepository.deleteById(id);
    }
}